import pymysql
pymysql.install_as_MySQLdb()

from flask import Flask, render_template, request, redirect, url_for, flash, session, send_file
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
from flask_mail import Mail, Message
import random
import string
import os
from werkzeug.utils import secure_filename
from config import *
import io
from PIL import Image  # 添加 Pillow 库

app = Flask(__name__, static_folder=STATIC_FOLDER)
app.config['SECRET_KEY'] = SECRET_KEY
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = SQLALCHEMY_TRACK_MODIFICATIONS

# 邮件配置
app.config['MAIL_SERVER'] = MAIL_SERVER
app.config['MAIL_PORT'] = MAIL_PORT
app.config['MAIL_USE_TLS'] = MAIL_USE_TLS
app.config['MAIL_USERNAME'] = MAIL_USERNAME
app.config['MAIL_PASSWORD'] = MAIL_PASSWORD

# 文件上传配置
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

mail = Mail(app)

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def generate_verify_code():
    """生成6位验证码"""
    return ''.join(random.choices(string.digits, k=6))

def send_verify_email(email, verify_code):
    """发送验证邮件"""
    try:
        msg = Message('烟蒂绿航站 - 邮箱验证',
                    sender=app.config['MAIL_USERNAME'],
                    recipients=[email])
        msg.body = f'''欢迎加入烟蒂绿航站！
您的验证码是：{verify_code}
请在注册页面输入此验证码完成验证。
'''
        mail.send(msg)
        # 添加绿色调试信息
        print(f"\033[92m发送验证码到 {email}: {verify_code}\033[0m")
        return True
    except Exception as e:
        print(f"发送邮件失败: {str(e)}")
        return False

# 上下文处理器：为所有模板提供购物车数量
@app.context_processor
def inject_cart_count():
    if current_user.is_authenticated:
        cart_count = CartItem.query.filter_by(user_id=current_user.id).count()
        return {'cart_count': cart_count}
    return {'cart_count': 0}

# 用户模型
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True, nullable=False)
    username = db.Column(db.String(80), nullable=False)
    password = db.Column(db.String(120), nullable=False)
    points = db.Column(db.Integer, default=0)
    cigarette_butts = db.Column(db.Integer, default=0)
    avatar = db.Column(db.String(200), nullable=True, default=DEFAULT_AVATAR)  # 存储默认头像路径
    avatar_data = db.Column(db.LargeBinary(length=(2**24)-1), nullable=True)  # 使用 MEDIUMBLOB 类型
    avatar_mimetype = db.Column(db.String(30), nullable=True)  # 存储头像的MIME类型
    gender = db.Column(db.String(10), nullable=True)  # 性别
    age = db.Column(db.Integer, nullable=True)       # 添加年龄字段
    city = db.Column(db.String(50), nullable=True)   # 城市
    bio = db.Column(db.Text, nullable=True)          # 个人简介
    created_at = db.Column(db.DateTime, nullable=False, default=db.func.current_timestamp())

# 积分记录模型
class PointRecord(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    points = db.Column(db.Integer, nullable=False)  # 积分变动值
    record_type = db.Column(db.String(50), nullable=False)  # 记录类型
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)

# 商品模型
class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    points_required = db.Column(db.Integer, nullable=False)
    description = db.Column(db.Text)
    image = db.Column(db.String(500))  # 商品图片路径
    stock = db.Column(db.Integer, default=0)  # 库存数量
    category = db.Column(db.String(50))  # 商品分类
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    cart_items = db.relationship('CartItem', backref='product', lazy=True)

# 购物车模型
class CartItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
    quantity = db.Column(db.Integer, default=1)
    created_at = db.Column(db.DateTime, default=datetime.now)

# 订单模型
class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    total_points = db.Column(db.Integer, nullable=False)
    status = db.Column(db.String(20), nullable=False, default='completed')  # completed, cancelled
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
    order_items = db.relationship('OrderItem', backref='order', lazy=True)

class OrderItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    order_id = db.Column(db.Integer, db.ForeignKey('order.id'), nullable=False)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
    quantity = db.Column(db.Integer, nullable=False)
    points = db.Column(db.Integer, nullable=False)  # 兑换时的积分价格

# 在 User 模型后添加 EmailVerification 模型
class EmailVerification(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True, nullable=False)
    verify_code = db.Column(db.String(6), nullable=False)
    is_verified = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, nullable=False, default=db.func.current_timestamp())

def compress_image(file_data, max_size=(800, 800)):
    """压缩图片函数"""
    try:
        # 从二进制数据创建图片对象
        img = Image.open(io.BytesIO(file_data))
        
        # 如果图片是 RGBA 模式，转换为 RGB
        if img.mode == 'RGBA':
            img = img.convert('RGB')
        
        # 调整图片大小
        img.thumbnail(max_size, Image.LANCZOS)
        
        # 保存压缩后的图片到内存
        output = io.BytesIO()
        img.save(output, format='JPEG', quality=85)
        output.seek(0)
        
        return output.getvalue()
    except Exception as e:
        print(f"\033[91m图片压缩失败: {str(e)}\033[0m")
        return None

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# 注册路由
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        email = request.form.get('email')
        username = request.form.get('username')
        password = request.form.get('password')
        verify_code = request.form.get('verify_code')

        # 检查邮箱是否已验证
        verification = EmailVerification.query.filter_by(email=email).first()
        if not verification or not verification.is_verified:
            flash('请先完成邮箱验证', 'error')
            return redirect(url_for('register'))

        # 检查邮箱是否已被注册
        if User.query.filter_by(email=email).first():
            flash('该邮箱已被注册', 'error')
            return redirect(url_for('register'))

        # 创建新用户
        new_user = User(email=email, username=username, password=password)
        db.session.add(new_user)
        
        try:
            db.session.commit()
            flash('注册成功！请登录', 'success')
            return redirect(url_for('login'))
        except Exception as e:
            db.session.rollback()
            flash('注册失败，请稍后重试', 'error')
            return redirect(url_for('register'))
            
    return render_template('register.html')

# 登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        email = request.form.get('email')
        password = request.form.get('password')
        
        user = User.query.filter_by(email=email).first()
        
        if user and user.password == password:
            login_user(user)
            return redirect(url_for('dashboard'))
        else:
            flash('邮箱或密码错误', 'error')
            
    return render_template('login.html')

# 用户面板
@app.route('/dashboard')
@login_required
def dashboard():
    # 获取积分排行榜前十名
    top_users = User.query.order_by(User.points.desc()).limit(10).all()
    # 获取用户最近的积分记录
    point_records = PointRecord.query.filter_by(user_id=current_user.id).order_by(PointRecord.created_at.desc()).limit(10).all()
    
    return render_template('dashboard.html', 
                         points=current_user.points,
                         top_users=top_users,
                         point_records=point_records)

# 添加用户详情页面路由
@app.route('/user/<int:user_id>')
@login_required
def user_detail(user_id):
    user = User.query.get_or_404(user_id)
    # 获取该用户的积分记录
    point_records = PointRecord.query.filter_by(user_id=user_id).order_by(PointRecord.created_at.desc()).limit(20).all()
    return render_template('user_detail.html', user=user, point_records=point_records)

# 商品列表
@app.route('/products')
@login_required
def products():
    products = Product.query.all()
    # 获取用户购物车中的商品数量
    cart_count = CartItem.query.filter_by(user_id=current_user.id).count()
    return render_template('products.html', products=products, cart_count=cart_count)

# 购物车相关路由
@app.route('/cart')
@login_required
def view_cart():
    cart_items = CartItem.query.filter_by(user_id=current_user.id).all()
    total_points = sum(item.product.points_required * item.quantity for item in cart_items)
    return render_template('cart.html', cart_items=cart_items, total_points=total_points)

@app.route('/cart/add/<int:product_id>', methods=['POST'])
@login_required
def add_to_cart(product_id):
    product = Product.query.get_or_404(product_id)
    quantity = int(request.form.get('quantity', 1))
    
    # 检查库存
    if quantity > product.stock:
        flash('库存不足')
        return redirect(url_for('products'))
    
    # 检查积分是否足够
    total_points_required = product.points_required * quantity
    if total_points_required > current_user.points:
        flash('积分不足')
        return redirect(url_for('products'))
    
    cart_item = CartItem.query.filter_by(user_id=current_user.id, product_id=product_id).first()
    
    if cart_item:
        cart_item.quantity += quantity
    else:
        cart_item = CartItem(user_id=current_user.id, product_id=product_id, quantity=quantity)
        db.session.add(cart_item)
    
    try:
        db.session.commit()
        flash(f'已添加 {quantity} 个{product.name}到购物车')
    except Exception as e:
        db.session.rollback()
        flash('添加失败，请重试')
    
    return redirect(url_for('products'))

@app.route('/cart/update/<int:item_id>', methods=['POST'])
@login_required
def update_cart(item_id):
    cart_item = CartItem.query.get_or_404(item_id)
    if cart_item.user_id != current_user.id:
        return {'error': '无权操作'}, 403
    
    quantity = request.form.get('quantity', type=int)
    if quantity <= 0:
        db.session.delete(cart_item)
    else:
        cart_item.quantity = quantity
    
    db.session.commit()
    return redirect(url_for('view_cart'))

@app.route('/cart/remove/<int:item_id>', methods=['POST'])
@login_required
def remove_from_cart(item_id):
    cart_item = CartItem.query.get_or_404(item_id)
    if cart_item.user_id != current_user.id:
        return {'error': '无权操作'}, 403
    
    db.session.delete(cart_item)
    db.session.commit()
    flash('商品已从购物车移除')
    return redirect(url_for('view_cart'))

# 退出路由
@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('您已成功退出')
    return redirect(url_for('login'))

#/test/update_butts/<username>/<int:butts>
@app.route('/test/update_butts/<username>/<int:butts>')
def test_update_butts(username, butts):
    """
    测试函数：更新指定用户的已回收烟蒂数量
    :param username: 用户名
    :param butts: 新增烟蒂数量
    :return: JSON响应
    """
    user = User.query.filter_by(username=username).first()
    if not user:
        return {'error': '用户不存在'}, 404
    
    # 更新烟蒂数量和积分（1烟蒂=10积分）
    points_earned = butts * 10
    user.cigarette_butts += butts
    user.points += points_earned
    
    # 创建积分记录
    point_record = PointRecord(
        user_id=user.id,
        points=points_earned,
        record_type='烟蒂回收',
    )
    db.session.add(point_record)
    
    try:
        db.session.commit()
        return {
            'success': True,
            'message': f'成功添加{butts}个烟蒂',
            'user': {
                'username': user.username,
                'total_butts': user.cigarette_butts,
                'total_points': user.points
            }
        }
    except Exception as e:
        db.session.rollback()
        return {'error': str(e)}, 500

@app.route('/cart/checkout', methods=['POST'])
@login_required
def checkout():
    cart_items = CartItem.query.filter_by(user_id=current_user.id).all()
    if not cart_items:
        flash('购物车为空')
        return redirect(url_for('view_cart'))
    
    # 计算总积分
    total_points = sum(item.product.points_required * item.quantity for item in cart_items)
    
    # 检查积分是否足够
    if current_user.points < total_points:
        flash('积分不足')
        return redirect(url_for('view_cart'))
    
    # 检查库存
    for item in cart_items:
        if item.quantity > item.product.stock:
            flash(f'{item.product.name} 库存不足')
            return redirect(url_for('view_cart'))
    
    try:
        # 创建订单
        order = Order(
            user_id=current_user.id,
            total_points=total_points
        )
        db.session.add(order)
        
        # 创建订单项目并更新库存
        for item in cart_items:
            order_item = OrderItem(
                order_id=order.id,
                product_id=item.product.id,
                quantity=item.quantity,
                points=item.product.points_required
            )
            db.session.add(order_item)
            
            # 更新库存
            item.product.stock -= item.quantity
            
            # 删除购物车项目
            db.session.delete(item)
        
        # 扣除用户积分
        current_user.points -= total_points
        
        # 创建积分记录
        point_record = PointRecord(
            user_id=current_user.id,
            points=-total_points,
            record_type='商品兑换'
        )
        db.session.add(point_record)
        
        db.session.commit()
        flash('兑换成功！')
        return redirect(url_for('dashboard'))
        
    except Exception as e:
        db.session.rollback()
        flash('兑换失败，请重试')
        return redirect(url_for('view_cart'))

# 添加根路由
@app.route('/')
def index():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))
    return redirect(url_for('login'))

@app.route('/reset_password', methods=['GET', 'POST'])
def reset_password():
    if request.method == 'POST':
        email = request.form.get('email')
        verify_code = request.form.get('verify_code')
        new_password = request.form.get('new_password')
        
        # 打印调试信息
        print(f"\033[94m重置密码请求:")
        print(f"邮箱: {email}")
        print(f"验证码: {verify_code}")
        print(f"新密码: {'*' * len(new_password)}\033[0m")
        
        user = User.query.filter_by(email=email).first()
        if not user:
            flash('该邮箱未注册')
            return redirect(url_for('reset_password'))
            
        # 验证验证码
        verification = EmailVerification.query.filter_by(email=email).first()
        if not verification:
            flash('请先获取验证码')
            return redirect(url_for('reset_password'))
            
        print(f"\033[93m数据库中的验证码: {verification.verify_code}\033[0m")
            
        if verification.verify_code != verify_code:
            flash('验证码错误')
            return redirect(url_for('reset_password'))
            
        # 更新密码
        user.password = new_password
        verification.is_verified = True
        db.session.commit()
        
        flash('密码重置成功，请登录')
        return redirect(url_for('login'))
        
    return render_template('reset_password.html')

# 添加邮箱验证相关路由
@app.route('/send_code', methods=['POST'])
def send_code():
    """发送验证码接口"""
    email = request.form.get('email')
    if not email:
        return {'success': False, 'message': '请输入邮箱地址'}

    # 生成验证码
    verify_code = generate_verify_code()
    
    # 保存或更新验证记录
    verification = EmailVerification.query.filter_by(email=email).first()
    if verification:
        verification.verify_code = verify_code
        verification.is_verified = False
    else:
        verification = EmailVerification(email=email, verify_code=verify_code)
        db.session.add(verification)
    
    try:
        db.session.commit()
        # 发送验证邮件
        if send_verify_email(email, verify_code):
            return {'success': True, 'message': '验证码已发送到您的邮箱'}
        else:
            return {'success': False, 'message': '发送验证码失败，请稍后重试'}
    except Exception as e:
        db.session.rollback()
        return {'success': False, 'message': str(e)}

@app.route('/verify_code', methods=['POST'])
def verify_code():
    """验证验证码接口"""
    email = request.form.get('email')
    code = request.form.get('verify_code')
    
    if not email or not code:
        return {'success': False, 'message': '请输入邮箱和验证码'}
    
    verification = EmailVerification.query.filter_by(email=email).first()
    if not verification:
        return {'success': False, 'message': '未找到验证记录'}
    
    if verification.verify_code != code:
        return {'success': False, 'message': '验证码错误'}
    
    # 验证成功，更新状态
    verification.is_verified = True
    db.session.commit()
    
    return {'success': True, 'message': '验证成功'}

@app.route('/profile', methods=['GET', 'POST'])
@login_required
def profile():
    if request.method == 'POST':
        if 'avatar' in request.files:
            file = request.files['avatar']
            if file and allowed_file(file.filename):
                try:
                    print("\033[92m开始处理头像上传")
                    file_data = file.read()
                    if file_data:
                        # 压缩图片
                        compressed_data = compress_image(file_data)
                        if compressed_data:
                            # 直接更新数据库
                            db.session.execute(
                                db.text('UPDATE user SET avatar_data = :data, avatar_mimetype = :mime WHERE id = :id'),
                                {'data': compressed_data, 'mime': 'image/jpeg', 'id': current_user.id}
                            )
                            db.session.commit()
                            print(f"头像已更新: 大小={len(compressed_data)}字节")
                            flash('头像更新成功！')
                        else:
                            flash('图片处理失败')
                        return redirect(url_for('profile'))
                except Exception as e:
                    db.session.rollback()
                    print(f"\033[91m头像更新失败: {str(e)}\033[0m")
                    flash('头像更新失败')
                    return redirect(url_for('profile'))

        # 更新其他信息
        try:
            current_user.username = request.form.get('username')
            current_user.gender = request.form.get('gender')
            current_user.city = request.form.get('city')
            current_user.bio = request.form.get('bio')
            
            age = request.form.get('age')
            if age and age.isdigit():
                current_user.age = int(age)
            
            db.session.commit()
            flash('个人资料更新成功！')
        except Exception as e:
            db.session.rollback()
            flash('更新失败，请稍后重试')

    return render_template('profile.html')

@app.route('/avatar/<int:user_id>')
def get_avatar(user_id):
    try:
        # 直接从数据库获取头像数据
        result = db.session.execute(
            db.text('SELECT avatar_data, avatar_mimetype FROM user WHERE id = :id'),
            {'id': user_id}
        ).first()
        
        if result and result[0]:
            print(f"\033[94m获取用户 {user_id} 的头像: {len(result[0])} 字节\033[0m")
            return send_file(
                io.BytesIO(result[0]),
                mimetype=result[1]
            )
    except Exception as e:
        print(f"\033[91m获取头像失败: {str(e)}\033[0m")
    
    return redirect(url_for('static', filename=DEFAULT_AVATAR))

@app.route('/leaderboard')
@login_required
def leaderboard():
    # 获取所有用户并按积分排序
    users = User.query.order_by(User.points.desc()).all()
    return render_template('leaderboard.html', users=users)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    if not os.environ.get('WERKZEUG_RUN_MAIN'):
        print("""
            \033[91m公网域名:https://c6a345d.r37.cpolar.top/
            积分增加测试接口:
            https://c6a345d.r37.cpolar.top/test/update_butts/<username>/<int:butts>\033[0m""")
    # 修改这里，host='0.0.0.0' 表示监听所有网络接口
    app.run(host='0.0.0.0', port=5000, debug=True)
    